package com.mybatistest.common;

import com.mybatistest.entity.Mybatis;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.TooManyResultsException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

@RestControllerAdvice
@Slf4j
public class GlobalException {
    @ExceptionHandler(TooManyResultsException.class)
    public ResponseData<Mybatis> handleTooManyResultsException(TooManyResultsException e) {
        log.error("TooManyResultsException", e);
        return ResponseData.fail("查找结果不唯一");
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseData<Mybatis> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        log.error("MethodArgumentTypeMismatchException", e);
        return ResponseData.fail("参数类型不匹配");
    }

    /**
     * 处理方法参数校验异常
     * 当方法参数注解(@RequestParam、@PathVariable等)校验失败时，会抛出此异常
     *
     * @param e           校验异常对象
     * @param request     HTTP请求对象
     * @return            返回校验错误的字段及错误信息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseData<Map<String, String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request) {
        log.error("MethodArgumentNotValidException", e);
        Map<String, String> result = new HashMap<>();
        BindingResult bindingResult = e.getBindingResult();
        log.error("请求[ {} ] {} 的参数校验发生错误", request.getMethod(), request.getRequestURL());
        for (ObjectError objectError : bindingResult.getAllErrors()) {
            FieldError fieldError = (FieldError) objectError;
            log.error("参数 {} = {} 校验错误：{}", fieldError.getField(), fieldError.getRejectedValue(), fieldError.getDefaultMessage());
            result.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return ResponseData.fail("参数校验发生错误", result);
    }

    /**
     * 处理约束违反异常
     * 当JSR303注解校验失败时，会抛出此异常
     *
     * @param e           约束违反异常对象
     * @param request     HTTP请求对象
     * @return            返回校验错误的字段及错误信息
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseData<Map<String, String>> handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
        log.error("ConstraintViolationException", e);
        Map<String, String> result = new HashMap<>();
        e.getConstraintViolations().forEach(constraintViolation -> {
            log.error("参数 {} = {} 校验错误：{}", constraintViolation.getPropertyPath(), constraintViolation.getInvalidValue(), constraintViolation.getMessage());
            result.put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage());
        });
        return ResponseData.fail("参数校验发生错误",result);
    }
    @ExceptionHandler(DuplicateKeyException.class)
    public ResponseData<Mybatis> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("DuplicateKeyException", e);
        return ResponseData.fail("数据库中已存在相同记录");
    }
}
